﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.ComponentModel.DataAnnotations.Schema;
using System.Threading.Tasks;
using System.Threading;
using System.Linq.Expressions;
using System.Transactions;
using System.Data;
using Lynn.Infastructure.Utils;
using Lynn.Infastructure.Repository.BaseEntity;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.DependencyInjection;

namespace Lynn.Infastructure.Repository.EFCore
{
    [AppIgnore]
    public class EFCoreBaseContext : DbContext
    {

        public EFCoreBaseContext(DbContextOptions options) : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Database.AutoTransactionsEnabled = false;
            var models = this.GetCurrentModels();
            var types = GetCurrentConfigTypes();
            foreach (var item in models)
            {
                //var ts = types.Where(x => x.FullName.Contains(item.Name)).FirstOrDefault();
                Type t;
                if (types.TryGetValue(item.Name,out t))
                {
                    dynamic instance = Activator.CreateInstance(t);
                    modelBuilder.ApplyConfiguration(instance);
                }
                else {
                    MethodInfo mi = this.GetType().GetMethod("Method").MakeGenericMethod(new Type[] { item });
                    mi.Invoke(this, new object[] { modelBuilder });
                }
            }
            RemoveDeleteCascade(modelBuilder);
            base.OnModelCreating(modelBuilder);
        }
        /// <summary>
        /// 获取设置项
        /// </summary>
        /// <returns></returns>
        private Dictionary<string,Type> GetCurrentConfigTypes()
        {
            var types = AssemblyHelper.FindTypeInfos(x => x.BaseType != null);
            var dic = new Dictionary<string,Type>();
            foreach (var t in types) {
                var tmp = GetType(t);
                if (tmp == null) continue;
                if (tmp.GenericTypeArguments.Length == 0) continue;
                var type = tmp.GenericTypeArguments[0];
                if (type.Name == "T") continue;
                dic.Add(type.Name, t);
            }
            return dic;
        }
        private Type GetType(Type info) {
            var baseType = info.BaseType;
            while (baseType != null&&baseType.Name!= typeof(EFCoreBaseConfig<>).Name) {
                baseType= baseType.BaseType;
            }
            //var test= typeof(BaseConfig<>).IsAssignableFrom(info);
            return baseType;
        }
        /// <summary>
        /// 获取需要建表的实体
        /// </summary>
        /// <returns></returns>
        private IEnumerable<Type> GetCurrentModels()
        {
            var types = AssemblyHelper.FindTypeInfos(x => x.GetCustomAttribute<TableAttribute>() != null);
            return types;
        }
        public void Method<T>(ModelBuilder modelBuilder) where T : class
        {
            var instance = new EFCoreBaseConfig<T>();
            modelBuilder.ApplyConfiguration(instance);
        }
        /// <summary>
        /// 移除级联删除
        /// </summary>
        /// <param name="modelBuilder"></param>
        void RemoveDeleteCascade(ModelBuilder modelBuilder)
        {
            var fks = modelBuilder.Model.GetEntityTypes()
                                     .SelectMany(t => t.GetForeignKeys()).ToList();
            foreach (var fk in fks)
            {
                fk.DeleteBehavior = DeleteBehavior.Restrict;
            }
        }
        public override int SaveChanges()
        {
            EntryBuild();
            return base.SaveChanges();
        }
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            EntryBuild();
            return await base.SaveChangesAsync(cancellationToken);
        }
        private void EntryBuild()
        {
            this.ChangeTracker.DetectChanges();
            foreach (var entry in ChangeTracker.Entries())
            {
                switch (entry.State)
                {
                    case EntityState.Modified:
                        {
                            entry.State = EntityState.Modified;
                            if (entry.Entity is IRowVersion row)
                            {
                                row.RowVersion = row.RowVersion + 1;
                            }
                            if (entry.Entity is IUpdateTime update)
                            {
                                update.UpdateTime = DateTime.Now;
                            }
                            break;
                        }
                    case EntityState.Deleted:
                        {
                            if (entry.Entity is IDel e)
                            {
                                entry.State = EntityState.Modified;
                                e.Del = true;
                                if (entry.Entity is IUpdateTime update)
                                {
                                    update.UpdateTime = DateTime.Now;
                                }
                            }
                            break;
                        }
                }
            }
        }
    }
}
